import sys
import os
import json
from datetime import datetime

import pygame
import serial
import serial.tools.list_ports
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                            QHBoxLayout, QLabel, QPushButton, QTextEdit, 
                            QComboBox, QMessageBox, QTableWidget, QTableWidgetItem,
                            QTabWidget, QFormLayout, QSpinBox, QLineEdit, QDialog, QGroupBox)
from PySide6.QtCore import Qt, Signal, QThread as QThreadBase, QTimer
from PySide6.QtGui import QFont

import mysql.connector
from mysql.connector import pooling
import time

class QThread(QThreadBase):
    def __init__(self):
        super().__init__()
        self.running = True

class SerialThread(QThread):
    """串口数据读取线程"""
    data_received = Signal(str)
    
    def __init__(self, port, baudrate=9600):
        super().__init__()
        self.port = port
        self.baudrate = baudrate
        self.running = True
        
    def run(self):
        try:
            with serial.Serial(self.port, self.baudrate, timeout=1) as ser:
                buffer = ""
                while self.running:
                    if ser.in_waiting:
                        char = ser.read().decode('utf-8')
                        if char == '\r' or char == '\n':
                            if buffer:
                                self.data_received.emit(buffer)
                                buffer = ""
                        else:
                            buffer += char
        except Exception as e:
            print(f"串口读取错误: {str(e)}")
            
    def stop(self):
        self.running = False

class ScannerApp(QMainWindow):
    def __init__(self):
        try:
            print("开始初始化主窗口...")
            super().__init__()
            print("设置窗口标题...")
            self.setWindowTitle("二维码扫描系统")
            print("设置窗口大小...")
            self.setGeometry(100, 100, 1000, 800)
            # 设置窗口可调整大小
            print("设置窗口标志...")
            self.setWindowFlags(Qt.Window)
            
            # 设置全局字体
            print("设置全局字体...")
            font = QFont()
            font.setPointSize(12)
            QApplication.instance().setFont(font)
            
            # 初始化变量
            print("初始化基础变量...")
            self.base_path = "D:/数据存储"
            self.backup_path = os.path.join(self.base_path, "backup")
            self.config_file = os.path.join(self.base_path, "scan_config.json")
            self.serial_thread = None
            self.scan_rules = {
                'total_length': 0,
                'fixed_segments': []
            }
            
            # 确保目录存在
            print("创建必要目录...")
            self.ensure_directories()
            print("加载配置...")
            self.load_config()
            
            # 添加密码配置
            self.config_password = "admin186"
            self.load_password()
            
            # 设置界面
            print("设置用户界面...")
            self.setup_ui()
            
            # 初始化数据库相关变量
            print("初始化数据库变量...")
            self.db_config = {
                "host": "192.168.1.2",
                "user": "root",
                "password": "admin186",
                "database": "saoma"
            }
            
            # 验证数据库配置是否正确
            self.test_db_connection()
            
            # 定时检查串口
            print("设置串口检查定时器...")
            self.port_timer = QTimer()
            self.port_timer.timeout.connect(self.update_ports)
            self.port_timer.start(5000)  # 每5秒检查一次
            self.update_ports()
            
            print("初始化完成!")
            
        except Exception as e:
            print(f"初始化错误: {str(e)}")
            import traceback
            traceback.print_exc()
            raise

    def setup_ui(self):
        """设置主界面"""
        # 创建中央窗口
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        
        # 数据库状态区域
        db_status_layout = QHBoxLayout()
        self.db_status_label = QLabel("数据库状态: 未连接")
        self.db_status_indicator = QLabel()
        self.db_status_indicator.setFixedSize(20, 20)
        self.db_status_indicator.setStyleSheet("background-color: red; border-radius: 10px;")
        self.db_reconnect_btn = QPushButton("重新连接数据库")
        self.db_reconnect_btn.clicked.connect(self.test_db_connection)
        db_status_layout.addWidget(self.db_status_indicator)
        db_status_layout.addWidget(self.db_status_label)
        db_status_layout.addWidget(self.db_reconnect_btn)
        db_status_layout.addStretch()
        main_layout.addLayout(db_status_layout)
        
        # 创建选项卡
        self.tab_widget = QTabWidget()
        main_layout.addWidget(self.tab_widget)
        
        # 添加标签切换事件处理
        self.tab_widget.currentChanged.connect(self.on_tab_changed)
        
        # 扫描页面
        scan_tab = QWidget()
        scan_layout = QVBoxLayout(scan_tab)
        
        # 串口设置区域
        port_layout = QHBoxLayout()
        self.port_combo = QComboBox()
        self.device_combo = QComboBox()  # 新增设备选择下拉框
        self.device_combo.addItems([f"扫码设备{i}" for i in range(1, 6)])  # 添加设备选项
        self.connect_button = QPushButton("连接")
        self.connect_button.clicked.connect(self.toggle_connection)
        port_layout.addWidget(QLabel("串口:"))
        port_layout.addWidget(self.port_combo)
        port_layout.addWidget(QLabel("设备名称:"))  # 新增设备名称标签
        port_layout.addWidget(self.device_combo)  # 新增设备选择下拉框
        port_layout.addWidget(self.connect_button)
        port_layout.addStretch()
        scan_layout.addLayout(port_layout)
        
        # 状态显示
        self.status_label = QLabel("状态: 未连接")
        scan_layout.addWidget(self.status_label)
        
        # 扫描记录显示
        scan_layout.addWidget(QLabel("扫描记录:"))
        self.scan_text = QTextEdit()
        self.scan_text.setReadOnly(True)
        scan_layout.addWidget(self.scan_text)
        
        # 添加进入配置按钮
        config_button = QPushButton("进入配置")
        config_button.clicked.connect(self.show_password_dialog)
        scan_layout.addWidget(config_button)
        
        self.tab_widget.addTab(scan_tab, "扫描")
        
        # 配置页面 - 创建但不立即添加到tab_widget
        config_tab = QWidget()
        config_layout = QVBoxLayout(config_tab)
        
        # 总长度设置
        length_layout = QFormLayout()
        self.total_length_spin = QSpinBox()
        self.total_length_spin.setMaximum(999)
        self.total_length_spin.setValue(self.scan_rules['total_length'])
        length_layout.addRow("数据总长度:", self.total_length_spin)
        config_layout.addLayout(length_layout)
        
        # 固定段位设置
        config_layout.addWidget(QLabel("固定段位规则:"))
        self.rules_table = QTableWidget()
        self.rules_table.setColumnCount(3)
        self.rules_table.setHorizontalHeaderLabels(["起始位置", "长度", "固定值"])
        self.update_rules_table()
        config_layout.addWidget(self.rules_table)
        
        # 添加规则输入区域
        rule_input_layout = QHBoxLayout()
        self.start_pos_input = QSpinBox()
        self.length_input = QSpinBox()
        self.value_input = QLineEdit()
        rule_input_layout.addWidget(QLabel("起始位置:"))
        rule_input_layout.addWidget(self.start_pos_input)
        rule_input_layout.addWidget(QLabel("长度:"))
        rule_input_layout.addWidget(self.length_input)
        rule_input_layout.addWidget(QLabel("固定值:"))
        rule_input_layout.addWidget(self.value_input)
        config_layout.addLayout(rule_input_layout)
        
        # 规则操作按钮
        button_layout = QHBoxLayout()
        add_rule_btn = QPushButton("添加规则")
        add_rule_btn.clicked.connect(self.add_rule)
        delete_rule_btn = QPushButton("删除选中")
        delete_rule_btn.clicked.connect(self.delete_rule)
        save_config_btn = QPushButton("保存配置")
        save_config_btn.clicked.connect(self.save_config)
        button_layout.addWidget(add_rule_btn)
        button_layout.addWidget(delete_rule_btn)
        button_layout.addWidget(save_config_btn)
        config_layout.addLayout(button_layout)
        
        # 修改字体大小
        title_font = QFont()
        title_font.setPointSize(14)
        title_font.setBold(True)
        
        self.status_label.setFont(title_font)
        self.scan_text.setFont(QFont("Arial", 12))
        
        # 设置表格字体
        self.rules_table.setFont(QFont("Arial", 12))
        self.rules_table.horizontalHeader().setFont(title_font)
        
        self.config_tab = config_tab  # 保存配置页面的引用

    def ensure_directories(self):
        """确保必要的目录存在"""
        os.makedirs(self.base_path, exist_ok=True)
        os.makedirs(self.backup_path, exist_ok=True)

    def update_ports(self):
        """更新可用串口列表"""
        current_port = self.port_combo.currentText()
        self.port_combo.clear()
        ports = [port.device for port in serial.tools.list_ports.comports()]
        self.port_combo.addItems(ports)
        if current_port in ports:
            self.port_combo.setCurrentText(current_port)

    def toggle_connection(self):
        """切换串口连接状态"""
        if self.serial_thread is None:
            # 连接
            port = self.port_combo.currentText()
            if not port:
                QMessageBox.warning(self, "错误", "请选择串口")
                return
                
            try:
                self.serial_thread = SerialThread(port)
                self.serial_thread.data_received.connect(self.handle_scan_data)
                self.serial_thread.start()
                self.connect_button.setText("断开")
                self.status_label.setText("状态: 已连接")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"连接失败: {str(e)}")
                self.serial_thread = None
        else:
            # 断开
            self.serial_thread.stop()
            self.serial_thread.wait()
            self.serial_thread = None
            self.connect_button.setText("连接")
            self.status_label.setText("状态: 未连接")

    def handle_scan_data(self, data):
        """处理扫描数据"""
        try:
            # 检查是否设置了参数
            if self.scan_rules['total_length'] == 0 and not self.scan_rules['fixed_segments']:
                # 播放提示音
                audio_file = "saoma_ng.mp3"
                pygame.mixer.init(size=16)
                pygame.mixer.music.load(audio_file)
                pygame.mixer.music.play(1)
                
                QMessageBox.warning(self, "警告", "参数未设置")
                self.scan_text.append(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 参数未设置")
                return
                
            # 验证数据
            errors = self.validate_scan_data(data)
            status = "不合格" if errors else "合格"
            
            if errors:
                audio_file = "saoma_ng.mp3"
                pygame.mixer.init(size=16)
                pygame.mixer.music.load(audio_file)
                pygame.mixer.music.play(1)
                QMessageBox.warning(self, "验证失败", "\n".join(errors))
            else:
                audio_file = "saoma_ok.mp3"
                pygame.mixer.init(size=16)
                pygame.mixer.music.load(audio_file)
                pygame.mixer.music.play(1)
            
            # 保存数据到数据库
            self.save_to_database(data, status)
            
            # 更新显示
            current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            device_name = self.device_combo.currentText()
            self.scan_text.append(f"[{current_time}] {device_name} - {data} - {status}")
            
        except Exception as e:
            QMessageBox.critical(self, "错误", f"处理数据失败: {str(e)}")

    def test_db_connection(self):
        """测试数据库连接配置"""
        try:
            conn = mysql.connector.connect(**self.db_config)
            cursor = conn.cursor()
            
            # 确保数据表存在
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS erweima (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    datetime DATETIME,
                    erweima VARCHAR(255),
                    zhuangtai VARCHAR(10),
                    shebei VARCHAR(50)
                )
            """)
            conn.commit()
            
            cursor.close()
            conn.close()
            
            # 更新状态显示
            self.db_status_label.setText("数据库状态: 配置正确")
            self.db_status_indicator.setStyleSheet("background-color: green; border-radius: 10px;")
            print("数据库配置验证成功")
            
        except Exception as err:
            self.db_status_label.setText("数据库状态: 配置错误")
            self.db_status_indicator.setStyleSheet("background-color: red; border-radius: 10px;")
            print(f"数据库配置验证失败: {err}")
            QMessageBox.critical(self, "数据库配置错误", f"无法连接到数据库: {err}")

    def save_to_database(self, data, status):
        """保存扫描数据到数据库"""
        max_retries = 3
        retry_count = 0
        
        while retry_count < max_retries:
            conn = None
            cursor = None
            try:
                # 建立新的数据库连接
                conn = mysql.connector.connect(**self.db_config)
                cursor = conn.cursor()
                
                sql = """
                    INSERT INTO erweima (datetime, erweima, zhuangtai, shebei)
                    VALUES (%s, %s, %s, %s)
                """
                device_name = self.device_combo.currentText()
                values = (datetime.now(), data, status, device_name)
                cursor.execute(sql, values)
                conn.commit()
                
                # 更新状态显示
                self.db_status_label.setText("数据库状态: 数据已保存")
                self.db_status_indicator.setStyleSheet("background-color: green; border-radius: 10px;")
                return
                
            except mysql.connector.Error as err:
                retry_count += 1
                print(f"保存数据失败 (尝试 {retry_count}/{max_retries}): {err}")
                
                if retry_count < max_retries:
                    time.sleep(1)  # 等待1秒后重试
                else:
                    self.db_status_label.setText("数据库状态: 保存失败")
                    self.db_status_indicator.setStyleSheet("background-color: red; border-radius: 10px;")
                    self.scan_text.append(f"保存数据失败: {err}")
                    QMessageBox.critical(self, "数据库错误", f"保存数据失败: {err}")
            
            finally:
                # 确保关闭数据库连接
                if cursor:
                    cursor.close()
                if conn:
                    conn.close()

    def validate_scan_data(self, data):
        """验证扫描数据"""
        errors = []
        
        # 检查总长度
        if self.scan_rules['total_length'] > 0:
            if len(data) != self.scan_rules['total_length']:
                errors.append(f"数据长度不符合要求：应为{self.scan_rules['total_length']}位，实际为{len(data)}位")

        # 检查固定段位
        for rule in self.scan_rules['fixed_segments']:
            start = rule['start']
            length = rule['length']
            expected = rule['value']
            
            if start + length > len(data):
                errors.append(f"数据长度不足，无法验证位置{start}起始的{length}位固定值")
                continue
                
            actual = data[start:start + length]
            if actual != expected:
                errors.append(f"位置{start}起始的{length}位固定值不匹配：应为{expected}，实际为{actual}")

        return errors

    def load_config(self):
        """加载配置"""
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    self.scan_rules = json.load(f)
        except Exception as e:
            print(f"加载配置失败: {str(e)}")

    def save_config(self):
        """保存配置"""
        try:
            self.scan_rules['total_length'] = self.total_length_spin.value()
            
            # 获取规则表格中的数据
            rules = []
            for row in range(self.rules_table.rowCount()):
                rules.append({
                    'start': int(self.rules_table.item(row, 0).text()),
                    'length': int(self.rules_table.item(row, 1).text()),
                    'value': self.rules_table.item(row, 2).text()
                })
            self.scan_rules['fixed_segments'] = rules
            
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(self.scan_rules, f, ensure_ascii=False, indent=2)
                
            QMessageBox.information(self, "成功", "配置已保存")
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存配置失败: {str(e)}")

    def update_rules_table(self):
        """更新规则表格"""
        self.rules_table.setRowCount(len(self.scan_rules['fixed_segments']))
        for i, rule in enumerate(self.scan_rules['fixed_segments']):
            self.rules_table.setItem(i, 0, QTableWidgetItem(str(rule['start'])))
            self.rules_table.setItem(i, 1, QTableWidgetItem(str(rule['length'])))
            self.rules_table.setItem(i, 2, QTableWidgetItem(rule['value']))

    def add_rule(self):
        """添加新规则"""
        try:
            start = self.start_pos_input.value()
            length = self.length_input.value()
            value = self.value_input.text().strip()
            
            if not value:
                raise ValueError("固定值不能为空")
                
            row = self.rules_table.rowCount()
            self.rules_table.insertRow(row)
            self.rules_table.setItem(row, 0, QTableWidgetItem(str(start)))
            self.rules_table.setItem(row, 1, QTableWidgetItem(str(length)))
            self.rules_table.setItem(row, 2, QTableWidgetItem(value))
            
            # 清空输入
            self.value_input.clear()
            self.start_pos_input.setValue(0)
            self.length_input.setValue(0)
            
        except Exception as e:
            QMessageBox.warning(self, "错误", str(e))

    def delete_rule(self):
        """删除选中的规则"""
        selected = self.rules_table.selectedItems()
        if not selected:
            QMessageBox.information(self, "提示", "请先选择要删除的规则")
            return
            
        rows = set(item.row() for item in selected)
        for row in sorted(rows, reverse=True):
            self.rules_table.removeRow(row)

    def closeEvent(self, event):
        """程序关闭时的清理工作"""
        if self.serial_thread:
            self.serial_thread.stop()
            self.serial_thread.wait()
        event.accept()

    def show_password_dialog(self):
        """显示密码输入对话框"""
        dialog = QDialog(self)
        dialog.setWindowTitle("输入密码")
        layout = QVBoxLayout(dialog)
        
        password_input = QLineEdit()
        password_input.setEchoMode(QLineEdit.Password)
        layout.addWidget(QLabel("请输入密码:"))
        layout.addWidget(password_input)
        
        button_box = QHBoxLayout()
        ok_button = QPushButton("确定")
        cancel_button = QPushButton("取消")
        button_box.addWidget(ok_button)
        button_box.addWidget(cancel_button)
        layout.addLayout(button_box)
        
        ok_button.clicked.connect(dialog.accept)
        cancel_button.clicked.connect(dialog.reject)
        
        if dialog.exec_() == QDialog.Accepted:
            if password_input.text() == self.config_password:
                self.show_config_tab()
            else:
                QMessageBox.warning(self, "错误", "密码错误")

    def on_tab_changed(self, index):
        """处理标签页切换事件"""
        if index == 0:  # 切换到扫描页面
            # 如果存在配置页面，则移除它
            if self.tab_widget.count() > 1:
                self.tab_widget.removeTab(1)

    def show_config_tab(self):
        """显示配置页面"""
        if self.tab_widget.count() == 1:  # 如果配置页面未添加
            self.tab_widget.addTab(self.config_tab, "配置")
            
            # 添加修改密码部分到配置页面
            if not hasattr(self, 'password_group_added'):
                password_group = QGroupBox("密码设置")
                password_layout = QVBoxLayout()
                
                # 密码输入框
                password_form = QFormLayout()
                self.old_password = QLineEdit()
                self.old_password.setEchoMode(QLineEdit.Password)
                self.new_password = QLineEdit()
                self.new_password.setEchoMode(QLineEdit.Password)
                self.confirm_password = QLineEdit()
                self.confirm_password.setEchoMode(QLineEdit.Password)
                
                password_form.addRow("原密码:", self.old_password)
                password_form.addRow("新密码:", self.new_password)
                password_form.addRow("确认密码:", self.confirm_password)
                
                # 修改密码按钮
                change_password_btn = QPushButton("修改密码")
                change_password_btn.clicked.connect(self.change_password)
                
                password_layout.addLayout(password_form)
                password_layout.addWidget(change_password_btn)
                password_group.setLayout(password_layout)
                
                # 将密码设置添加到配置页面
                self.config_tab.layout().addWidget(password_group)
                self.password_group_added = True
        
        self.tab_widget.setCurrentWidget(self.config_tab)

    def change_password(self):
        """修改密码"""
        old_pwd = self.old_password.text()
        new_pwd = self.new_password.text()
        confirm_pwd = self.confirm_password.text()
        
        if old_pwd != self.config_password:
            QMessageBox.warning(self, "错误", "原密码错误")
            return
            
        if not new_pwd:
            QMessageBox.warning(self, "错误", "新密码不能为空")
            return
            
        if new_pwd != confirm_pwd:
            QMessageBox.warning(self, "错误", "两次输入的密码不一致")
            return
            
        self.config_password = new_pwd
        self.save_password()
        
        # 清空输入框
        self.old_password.clear()
        self.new_password.clear()
        self.confirm_password.clear()
        
        QMessageBox.information(self, "成功", "密码修改成功")

    def save_password(self):
        """保存密码到配置文件"""
        try:
            config_dir = os.path.dirname(self.config_file)
            password_file = os.path.join(config_dir, "password.json")
            with open(password_file, 'w') as f:
                json.dump({"password": self.config_password}, f)
        except Exception as e:
            print(f"保存密码失败: {str(e)}")

    def load_password(self):
        """从配置文件加载密码"""
        try:
            config_dir = os.path.dirname(self.config_file)
            password_file = os.path.join(config_dir, "password.json")
            if os.path.exists(password_file):
                with open(password_file, 'r') as f:
                    data = json.load(f)
                    self.config_password = data.get("password", "admin186")
        except Exception as e:
            print(f"加载密码失败: {str(e)}")

def main():
    """主程序入口"""
    try:
        print("正在启动应用程序...")
        # 检查是否已存在QApplication实例
        app = QApplication.instance()
        if app is None:
            app = QApplication(sys.argv)
        
        print("正在创建主窗口...")
        window = ScannerApp()
        print("正在显示主窗口...")
        window.show()
        print("进入事件循环...")
        return app.exec()
    except Exception as e:
        print(f"程序启动错误: {str(e)}")
        import traceback
        traceback.print_exc()
        return 1

if __name__ == "__main__":
    try:
        print("程序开始运行...")
        sys.exit(main())
    except Exception as e:
        print(f"程序崩溃: {str(e)}")
        import traceback
        traceback.print_exc()
        sys.exit(1) 